/**
 * IPTablesTrafficSource.java 13.07.2006
 */
package org.dicr.traffic.source.impl;

import java.util.*;

import javax.management.*;

import org.apache.log4j.*;
import org.dicr.mng.exc.*;
import org.dicr.sys.linux.*;
import org.dicr.traffic.source.*;
import org.dicr.util.net.*;

/**
 * IPTables Traffic Source
 * 
 * @author <A href='http://dicr.org'>Igor A Tarasov</A>
 * @version 060712
 * @see IPTables
 */
public class IPTablesTrafficSource extends AbstractTrafficSource implements MBeanRegistration, IPTablesTrafficSourceMBean {
	/** Logger */
	protected static final Logger log = Logger.getLogger(IPTablesTrafficSource.class);

	/** Default JMX object name */
	public static final String DEFAULT_OBJECT_NAME = "org.dicr:service=traffic;type=source;name=iptables";

	/** Timer for task scheduling */
	private Timer timer = null;

	/** Traffic check and fire period in seconds */
	private int firePeriod = 60;

	/** Reset iptables counters on fire */
	protected boolean resetOnFire = true;

	/**
	 * Constructor
	 */
	public IPTablesTrafficSource() {
		super();
	}

	/**
	 * Constructor
	 * 
	 * @param seconds traffic fire period
	 */
	public IPTablesTrafficSource(final int seconds) {
		super();
		this.setFirePeriod(seconds);
	}

	/**
	 * Return fire period
	 * 
	 * @param seconds period in seconds for traffic firing (default 60 seconds)
	 */
	@Override
    public void setFirePeriod(final int seconds) {
		if (seconds <= 0) throw new IllegalArgumentException("seconds: " + seconds);
		this.firePeriod = seconds;
		IPTablesTrafficSource.log.info("configured traffic fire period: " + seconds + " seconds");
	}

	/**
	 * Return fire period
	 * 
	 * @return period in seconds of traffic firing
	 */
	@Override
    public synchronized int getFirePeriod() {
		return this.firePeriod;
	}

	/**
	 * Return resetOnFire flag.
	 * 
	 * @return true if each time, after traffic get from iptables, iptables counters will reset to zero. false if
	 *         traffic counters always increment. default is true (reset each time traffic is getted).
	 */
	@Override
    public final boolean isResetOnFire() {
		return this.resetOnFire;
	}

	/**
	 * Set resetOnFire flag.
	 * 
	 * @param reset true to reset iptables counters after traffic getting. false to not reset. default true.
	 */
	@Override
    public final void setResetOnFire(final boolean reset) {
		this.resetOnFire = reset;
		IPTablesTrafficSource.log.info("configured resetOnFire flag: " + reset);
	}

	/**
	 * Fire traffic to sources
	 * 
	 * @param counters iptables counters
	 */
	protected final void fireTraffic(final Map<Net, IPTablesData> counters) {
		if (counters == null) throw new IllegalArgumentException("null counters");
		final List<TrafficElement> traffic = new ArrayList<>();
		for (final IPTablesData counter : counters.values()) {
			final Collection<TrafficElement> elements = counter.toTraffic();
			for (final TrafficElement element : elements) {
				if (element.getBytes() > 0 || !this.isSkipEmpty()) traffic.add(element);
			}
		}
		this.fireTraffic(traffic);
	}

	/**
	 * Check if source is running
	 * 
	 * @return if iptables task sheduled on specified period
	 */
	@Override
    public final boolean isRunning() {
		return this.timer != null;
	}

	/**
	 * Start iptables traffic source. Shedule iptables timer task on specified period.
	 */
	@Override
    public synchronized final void start() {
		if (!this.isRunning()) {
			IPTablesTrafficSource.log.info("starting iptables traffic source, fire period: " + this.firePeriod
			        + " seconds");
			this.timer = new Timer("IPTables traffic source", false);
			this.timer.scheduleAtFixedRate(new IPTablesTask(), this.firePeriod * 1000, this.firePeriod * 1000);
		} else {
			IPTablesTrafficSource.log.debug("iptables traffic source already running");
		}
	}

	/**
	 * Stop iptables traffic source. Cansel iptables timer task.
	 */
	@Override
    public synchronized final void stop() {
		if (this.isRunning()) {
			IPTablesTrafficSource.log.info("stopping iptables traffic source");
			this.timer.cancel();
			this.timer = null;
		} else {
			IPTablesTrafficSource.log.debug("traffic source is not running already");
		}
	}

	/**
	 * IPTables task
	 */
	protected class IPTablesTask extends TimerTask {
		private final IPTables iptables = IPTables.getInstance();

		/** Run task */
		@Override
		public void run() {
			try {
				IPTablesTrafficSource.this.fireTraffic(this.iptables.getTraf(IPTablesTrafficSource.this.resetOnFire));
			} catch (final CommandException ex) {
				IPTablesTrafficSource.log.error("error getting iptables counters", ex);
			}
		}
	}

	/**
	 * @see javax.management.MBeanRegistration#preRegister(javax.management.MBeanServer, javax.management.ObjectName)
	 */
	@Override
    public ObjectName preRegister(final MBeanServer server, final ObjectName name) throws Exception {
		return name != null ? name : ObjectName.getInstance(IPTablesTrafficSource.DEFAULT_OBJECT_NAME);
	}

	/**
	 * @see javax.management.MBeanRegistration#postRegister(java.lang.Boolean)
	 */
	@Override
    public void postRegister(final Boolean registrationDone) {
		if (registrationDone != null && registrationDone.booleanValue() && !this.isRunning()) this.start();
	}

	/**
	 * @see javax.management.MBeanRegistration#preDeregister()
	 */
	@Override
    public void preDeregister() throws Exception {
		if (this.isRunning()) this.stop();
	}

	/**
	 * @see javax.management.MBeanRegistration#postDeregister()
	 */
	@Override
    public void postDeregister() {
	// NOP
	}

}
